package com.example.user.service.transaction;

import com.example.framework.common.exception.ServiceException;
import com.example.user.dal.dataobject.user.UserDO;
import com.example.user.service.async.AsyncService;
import com.example.user.service.user.UserServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;

@Slf4j
@Service
public class TransactionService {

    @Resource
    private AsyncService asyncService;

    @Resource
    private UserServiceImpl userService;

    @Resource
    private ThreadPoolExecutor threadPoolExecutor;

    @Resource
    private PlatformTransactionManager transactionManager;

    @Transactional(rollbackFor = Exception.class)
    public void testTransaction() {
        UserDO entity = buildUserDO("测试事务");
        userService.save(entity);
        asyncService.test();
        throw new RuntimeException();
    }

    @Transactional
    public void concurrentTransaction1() {
        UserDO entity1 = buildUserDO("主线程创建");
        userService.save(entity1);
        new Thread(() -> {
            UserDO entity2 = buildUserDO("子线程创建");
            userService.save(entity2);
        }).start();
        // 由于事务和线程绑定，不同线程获取的数据库连接不同，属于不同事务
        // 因此主线程回滚，子线程不回滚
        throw ServiceException.of("手动抛出异常");
    }

    @Transactional
    public void concurrentTransaction2() {
        UserDO entity1 = buildUserDO("主线程创建");
        userService.save(entity1);

        List<UserDO> entityList = buildUserDOList("主数据");
        CountDownLatch latch = new CountDownLatch(entityList.size());
        // 异常标识，子线程报错时置为true
        AtomicBoolean isError = new AtomicBoolean(false);
        for (UserDO entity : entityList) {
            threadPoolExecutor.execute(() -> {
                // 每个对象都创建一个线程insert
                TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
                transactionTemplate.executeWithoutResult(status -> {
                    try {
                        // 通过TransactionTemplate.execute()执行insert
                        userService.save(entity);
                        if (entity.getName().contains("5")) {
                            throw new RuntimeException("主数据5出现异常，回滚");
                        }
                    } catch (Exception e) {
                        log.error(e.getMessage());
                        isError.set(true);
                    } finally {
                        latch.countDown();
                    }
                    // 等待其他线程执行
                    try {
                        latch.await();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    if (isError.get()) {
                        log.error("出现异常，回滚");
                        status.setRollbackOnly();
                    }
                });
            });
        }
        try {
            // 主线程等待其他子线程执行完毕
            latch.await();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        throw new RuntimeException();
    }

    private UserDO buildUserDO(String name) {
        return UserDO.builder()
                .userId(String.valueOf(UUID.randomUUID()))
                .name(name)
                .build();
    }

    private List<UserDO> buildUserDOList(String name) {
        List<UserDO> entityList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            UserDO entity = UserDO.builder()
                    .userId(String.valueOf(UUID.randomUUID()))
                    .name(name + i)
                    .build();
            entityList.add(entity);
        }
        return entityList;
    }
}
